package team.archai.nexus.boot.web.interceptors;

import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.AsyncHandlerInterceptor;
import team.archai.nexus.boot.cache.entity.CacheKV;
import team.archai.nexus.boot.cache.support.CacheHelper;
import team.archai.nexus.boot.web.annotation.SecureLimit;
import team.archai.nexus.boot.web.exception.SecureException;


import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;

/**
 * @program: tiny-dust-boot-build
 * @description: 接口请求次数限制拦截器
 * @author: lishibin
 * @create: 2020-07-08 22:19
 **/
@Component
@ConditionalOnProperty(name = "tiny-dust.secure.enabled", havingValue = "true")
public class SecureInterceptor implements AsyncHandlerInterceptor {

    @Resource
    private CacheHelper cacheHelper;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        /**
         * isAssignableFrom() 判定此 Class 对象所表示的类或接口与指定的 Class 参数所表示的类或接口是否相同，或是否是其超类或超接口
         * isAssignableFrom()方法是判断是否为某个类的父类
         * instanceof关键字是判断是否某个类的子类
         */
        if (handler.getClass().isAssignableFrom(HandlerMethod.class)) {
            //HandlerMethod 封装方法定义相关的信息,如类,方法,参数等
            HandlerMethod handlerMethod = (HandlerMethod) handler;
            Method method = handlerMethod.getMethod();
            // 获取方法中是否包含注解
            SecureLimit methodAnnotation = method.getAnnotation(SecureLimit.class);
            //获取 类中是否包含注解，也就是controller 是否有注解
            SecureLimit classAnnotation = method.getDeclaringClass().getAnnotation(SecureLimit.class);
            // 如果 方法上有注解就优先选择方法上的参数，否则类上的参数
            SecureLimit secureLimit = methodAnnotation != null ? methodAnnotation : classAnnotation;
            if (secureLimit != null) {
                if (isLimit(request, secureLimit)) {
                    throw new SecureException("request access limit max.");
                }
            }
        }
        return true;
    }

    public boolean isLimit(HttpServletRequest request, SecureLimit secureLimit) {
        String limitKey = request.getServletPath() + request.getSession().getId();
        CacheKV cacheKv = CacheKV.builder().K(limitKey).expire(secureLimit.time()).build();
        CacheKV kv = cacheHelper.get(cacheKv);
        if (null != kv.getV() && (int) kv.getV() >= secureLimit.times()) {
            cacheHelper.incr(kv);
            return true;
        }
        cacheKv.setV(1);
        cacheKv.setExpire(secureLimit.time());
        cacheHelper.put(cacheKv);
        return false;
    }
}
